﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.MonoRail.Framework;
using Castle.MonoRail.Framework.Filters;
using Newtonsoft.Json;
using StateTheater.Models;
using StateTheaterDataModel;
using Castle.Core;
using Castle.MonoRail.Framework.Helpers;
using System.Text;
using System.Globalization;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web.Security;
using System.Windows.Media.Imaging;
using System.Data.Linq;

namespace StateTheater.Controllers
{

	[Serializable]
	public class ShowCredit
	{
		public string Title {get; set;}
		[JsonIgnore]
		public int ProductionID { get; set; }
		public string Credit { get; set; }
		public string Troupe { get; set; }
		[JsonIgnore]
		public string TroupeShortname { get; set; }
		[JsonIgnore]
		public int TroupeID { get; set; }
		public DateTime OpeningNight { get; set; }
	};

	[Layout("default"), Rescue("generalerror")]
	[Filter(ExecuteWhen.BeforeAction, typeof(MobileFilter))]
	public class PersonController : SmartDispatcherController
	{

		const string cookieName = "sixdegreesfrom";

		StateTheaterDataContext db;
		public PersonController(StateTheaterDataContext db)
		{
			this.db = db;
		}

		/// <summary>
		///    "/Person/Details.rails?id=nnnnn"
		/// also accessable as "/Person/nnnn.rails"
		/// also accessable as "/Person/nnnn/name-name/Display.rails"
		/// 
		/// Display full details of a person, including bio, photo, and recent works.
		/// </summary>
		/// <param name="id">The id.</param>
		public void Display(int id)
		{
			var pp = from p in db.Peoples
						 where p.PersonID == id
						 select p;
			People person = pp.FirstOrDefault();
			PropertyBag["Person"] = person;
			PropertyBag["ID"] = person.PersonID;

			PropertyBag["Name"] = person.FullName;
			PropertyBag["SEOname"] = Util.SEOEscape(person.FullName, true);
			PropertyBag["Bio"] = person.Bio;
			PropertyBag["ActorsEquity"] = person.ActorsEquity;

			var roles = from r in db.Roles
						where r.PersonID == id
						orderby r.Production.FirstPerformance descending
						select new ShowCredit
							   {
								   Title = r.Production.Play.Title,
								   ProductionID = r.ProductionID,
								   Credit = r.Role1,
								   Troupe = Util.AdjTitle(r.Production.Troupe.Name),
								   TroupeShortname = r.Production.Troupe.shortname,
								   TroupeID = r.Production.TroupeID,
								   OpeningNight = r.Production.FirstPerformance
							   };
			var lstRoles = roles.ToList();
			PropertyBag["Roles"] = lstRoles;
			PropertyBag["photo"] = !String.IsNullOrEmpty(person.Photo);

			/*
			 * SELECT Plays.Title, Productions.TroupeID, 
			 * Troupes.Name, Productions.ProductionID, 
			 * Productions.FirstPerformance, CreditNames.Credit
				FROM Troupes 
			 * INNER JOIN ((Plays 
			 *   INNER JOIN (CreditNames 
			 *      INNER JOIN PlayCredit ON (CreditNames.CreditID = PlayCredit.CreditID)) 
			 *   ON (Plays.PlayID = PlayCredit.PlayID)) 
			 *   INNER JOIN Productions ON (Plays.PlayID = Productions.PlayID)) 
			 * ON (Troupes.TroupeID = Productions.TroupeID)
				WHERE (((PlayCredit.PersonID)=@id))
				ORDER BY Productions.FirstPerformance DES
			 * 
			 */
	   
				var authors = from pr in db.Productions join pc in db.PlayCredits  on pr.Play.PlayID equals pc.PlayID
							  where pc.PersonID == id
							  orderby pr.FirstPerformance descending
							  select new ShowCredit
							   {
								   Title = pr.Play.Title,
								   ProductionID = pr.ProductionID,
								   Credit = pc.CreditName.Credit,
								   Troupe = pr.Troupe.Name,
								   TroupeShortname = pr.Troupe.shortname,
								   TroupeID = pr.TroupeID,
								   OpeningNight = pr.FirstPerformance
							   };

			var lstAuthors = authors.ToList();
			foreach (var credit in lstAuthors)
			{
				credit.Title = Util.AdjTitle(credit.Title);
				credit.Troupe = Util.AdjTitle(credit.Troupe);
			}

			PropertyBag["Authors"] = lstAuthors;
 
			var prodcred = from pc in db.ProductionCredits
						   where pc.PersonID == id
						   orderby pc.Production.FirstPerformance descending
						   select new ShowCredit
							   {
								   Title = pc.Production.Play.Title,
								   ProductionID = pc.ProductionID,
								   Credit = pc.CreditName.Credit,
								   Troupe = pc.Production.Troupe.Name,
								   TroupeShortname = pc.Production.Troupe.shortname,
								   TroupeID = pc.Production.TroupeID,
								   OpeningNight = pc.Production.FirstPerformance
							   };

			var lstProdCred = prodcred.ToList();
			foreach (var credit in lstProdCred)
			{
				credit.Title = Util.AdjTitle(credit.Title);
				credit.Troupe = Util.AdjTitle(credit.Troupe);
			}
			PropertyBag["Credits"] = lstProdCred;

			PropertyBag["sixdegrees"] = Request.ReadCookie(cookieName); //  ?? Session[cookieName];
			string user = this.Context.CurrentUser.Identity.Name;
			bool isAdmin = this.Context.CurrentUser.IsInRole("Admin");
//			if (isAdmin || person.Member_Name.Equals(user, StringComparison.CurrentCultureIgnoreCase))
			if (isAdmin || user.Equals(person.Member_Name, StringComparison.CurrentCultureIgnoreCase))
			{
				PropertyBag["allowEdit"] = true;
			}
			else
			{
				if (person.Member_Name == null)
				{
					if (String.IsNullOrEmpty(user))
						PropertyBag["unclaimedUnregister"] = true;
					else
						PropertyBag["unclaimedRegister"] = true;

				}
			}

		}

		public void Display(int id, string addCookie)
		{
			if (addCookie == "yes")
			{
				HttpCookie cookie = new HttpCookie(cookieName, id.ToString());
				Response.CreateCookie(cookie);
				this.Session.Add(cookieName, id.ToString());
			}
			Display(id);
		}


		/// <summary>
		/// "/Person/Find.rails"
		/// Display menu page and accept partial name (used by <see cref="Search"/>).
		/// </summary>
		public void Find() { }

		/// <summary>
		/// "/Person/Search.rails?partialName=xxxxxx"
		/// Searches the specified partial name.
		/// </summary>
		/// <param name="partialName">The partial name.</param>
		public void Search(string partialName)
		{
			PropertyBag["partialName"] = partialName;

			var q = from p in db.Peoples
					where p.LastName.Contains(partialName) || p.FirstName.Contains(partialName)
					orderby p.LastName, p.FirstName
					select new NameDetails
										   {
											   FirstName = p.FirstName,
											   LastName = p.LastName,
//											   FullName = p.FullName,
											   ID = p.PersonID,
											   HasPhoto = p.Photo==null ? false : p.Photo.Length > 0,
											   HasBio = p.Bio == null ? false : p.Bio.Length > 0
										   };

			var list = q.ToList();
			list.ForEach(p => p.SEOName = Util.SEOEscape(p.FullName,false));
			PropertyBag["items"] = list;
		}


		public void List()
		{
			List("A");
		}

		public void List(string letter)
		{
			char lett = letter[0];
			var people = from p in db.Peoples
						 where p.LastName.Substring(0, 1) == letter
						 orderby p.LastName, p.FirstName
						 select new NameDetails
												{
													FirstName = p.FirstName,
													LastName = p.LastName,
													ID = p.PersonID,
													HasPhoto = p.Photo == null ? false : p.Photo.Length > 0,
													HasBio = p.Bio == null ? false : p.Bio.Length > 0,
//													FullName = p.FirstName + " " + p.LastName,
													SEOName = Util.SEOEscape((p.FirstName ?? "")  + " " + (p.LastName ?? ""), true)
												};
			PropertyBag["letter"] = letter;
			PropertyBag["list"] = people.ToList();
			PropertyBag["text"] = "List(string letter)";
			PropertyBag["DispPerson"] = false;
		}
		public void List(string letter, int id)
		{
			List(letter);
			PropertyBag["text"] = "List(string letter, int id)";
		}

		public void List(int id)
		{
			Display(id);
			People person = PropertyBag["Person"] as People;
			PropertyBag["DispPerson"] = person;
			List(person.LastName.Substring(0, 1));
		// 	List(person.LastName[0]);   // Parameter must be a string.
		}

		public void Test()
		{
		}


		public void Run6Degrees(int from, int to)
		{
			Response.RemoveCookie(cookieName);
			Session.Remove(cookieName);

			var degrees = db.DegreesSeparation(from, to).ToList();
			degrees.ForEach(p => { p.Troupe = Util.AdjTitle(p.Troupe); });	 // p.Title = Util.AdjTitle(p.Title); 
			PropertyBag["Degrees"] = degrees;

			if (degrees.Count > 0)
			{
				PropertyBag["FromName"] = degrees[0].FromName;
				PropertyBag["ToName"] = degrees[degrees.Count - 1].ToName;
			}
			else
			{
				People p1 = db.Peoples.First(p => p.PersonID == from);
				People p2 = db.Peoples.First(p => p.PersonID == to);
				PropertyBag["FromName"] = p1.FullName;
				PropertyBag["ToName"] = p2.FullName;
			}
		}
		public void Run6Degrees(int from, int to, int type)
		{
			Run6Degrees(from, to);
			PropertyBag["type"] = type;
			RenderView("Run6degreesEdit");
		}

		public void Image(int id)
		{
			CancelView();
			int height = 0;
			int width =0;
			int size;
			if (Int32.TryParse(Params["size"], out size))
			{
				height = size;
				width = size;
			}
			else
			{
				Int32.TryParse(Params["height"], out height);
				Int32.TryParse(Params["width"], out width);
			}
			string filename  = (from per in db.Peoples
					where per.PersonID == id
					select @"people\"+per.Photo).FirstOrDefault() ?? "Actor2.jpg";
			filename= Path.Combine(Path.Combine(this.Context.ApplicationPhysicalPath, @"Content\images"),filename);


			Bitmap oBitmap = new Bitmap(filename);
			if (height != 0 || width != 0)
			{
				oBitmap=Util.ResizeImage(oBitmap, width, height);
			}
			Response.CachePolicy.SetExpires(DateTime.Today.AddYears(1));
#if (true)
			Response.ContentType = "image/jpg";
			oBitmap.Save(Response.OutputStream, ImageFormat.Jpeg);
#else
			Response.ContentType = "image/png";
			PngBitmapEncoder encoder = new PngBitmapEncoder();
			encoder.Interlace = PngInterlaceOption.On;
			encoder.Frames.Add(BitmapFrame.Create(oBitmap.));
			encoder.Save(Response.OutputStream,);


#endif
		}

		//------------------------------------------------------------------------------------------------------
		// Utility methods
		//------------------------------------------------------------------------------------------------------

		private Pair<string, List<ShowCredit>> GetBio(int id, int maxShows)
		{
			Pair<string, List<ShowCredit>> person = new Pair<string, List<ShowCredit>>("", null);
			if (id != 0)
			{
				var q = db.Peoples.SingleOrDefault(p => p.PersonID == id);

				var roles = from r in db.Roles
							where r.PersonID == id
							orderby r.Production.FirstPerformance descending
							select new ShowCredit
							{
								Title = r.Production.Play.Title,
								ProductionID = r.ProductionID,
								Credit = r.Role1,
								Troupe = r.Production.Troupe.Name,
								TroupeID = r.Production.TroupeID,
								OpeningNight = r.Production.FirstPerformance
							};

				List<ShowCredit> lstRoles;
				if (maxShows > 0)
					lstRoles = roles.Take(maxShows).ToList();
				else
					lstRoles = roles.ToList();

				foreach (var role in lstRoles)
				{
					role.Title = Util.AdjTitle(role.Title);
					role.Troupe = Util.AdjTitle(role.Troupe);
				}

				person = new Pair<string, List<ShowCredit>>(q.Bio, lstRoles);
			}
			return person;
		}

		static public BioCopy GetPendingCopy(StateTheaterDataContext db, int personid)
		{
			PeoplePending pp = db.PeoplePendings.SingleOrDefault(p => p.PersonID == personid);
			BioCopy bc = new BioCopy(pp);
			if (pp == null)
			{
				bc = (from p in db.Peoples
					  where p.PersonID == personid
					  select new BioCopy
					  {
						  PersonID = p.PersonID,
						  ActorsEquity = p.ActorsEquity,
						  Bio = p.Bio,
						  FirstName = p.FirstName,
						  LastName = p.LastName,
						  Photo = p.Photo
					  }
						  ).SingleOrDefault();
				if (bc != null)
				{
					pp = new PeoplePending()
					{
						ActorsEquity = bc.ActorsEquity,
						Bio = bc.Bio,
						FirstName = bc.FirstName,
						LastName = bc.LastName,
						PersonID = bc.PersonID,
						Photo = bc.Photo
					};
					db.PeoplePendings.InsertOnSubmit(pp);
					db.SubmitChanges();

					if (String.IsNullOrEmpty(bc.Photo))
						bc.Photo = null;
				}
			}
			return bc;
		}

		//------------------------------------------------------------------------------------------------------
		// Service methods
		//------------------------------------------------------------------------------------------------------

		[return: JSONReturnBinder]
		[Rescue(typeof(JsonRescueController))]
		public SelectList[] SearchJson(string query, int maxItems)
		{
			if (String.IsNullOrEmpty(query))
			{
				return new SelectList[0];
			}
			else
			{
				var q = (from p in db.Peoples
						 where (p.LastName + ", " + p.FirstName).StartsWith(query)
						 orderby p.LastName, p.FirstName
						 select new SelectList(p.LastName + ", " + p.FirstName, p.PersonID)
						 ).Take(maxItems).ToArray();

				return q;
			}
		}

		// requirement of js control using this:
		// "The result must return with one value on each line. The result is presented in the order the backend sends it."
		public void SearchText(string q, int limit)
		{
			if (String.IsNullOrEmpty(q))
			{
				RenderText("");
			}
			else
			{
				var query = (from p in db.Peoples
//                         where (query).CompareTo(p.LastName + ", " + p.FirstName) < 1       //                         where (p.LastName + ", " + p.FirstName) > (query)
						 where (p.LastName + ", " + p.FirstName).StartsWith(q)
						 orderby p.LastName, p.FirstName
						 select new Pair<int, string>(p.PersonID, p.LastName + ", " + p.FirstName)
						 ).Take(limit).ToList();

				query.ForEach(p=>RenderText(p.First+"|"+p.Second+Environment.NewLine));
			}

			this.HttpContext.Response.ContentType = "text/text";
			CancelView();
		}
		public void SearchText(string term)
		{
			SearchText(term, 10);
		}


		[return: JSONReturnBinder]
		[Rescue(typeof(JsonRescueController))]
		public SelectList[] SearchJson(string term)
		{
			return SearchJson(term, 10);
		}

//		[AjaxAction]
		[return: JSONReturnBinder]
		[Rescue(typeof(JsonRescueController))]
		public Pair<string, List<ShowCredit>> BioJson(int id)
		{
			if (id == 0)
			{
				return new Pair<string, List<ShowCredit>>( "", new List<ShowCredit>());
			}
			else
			{
				return GetBio(id, 5);
			}
		}

		[return: JSONReturnBinder]
		[Rescue(typeof(JsonRescueController))]
		public Status RequestLinkage(int personid)
		{
			var name = this.Context.CurrentUser.Identity.Name;

			var user = db.FORUM_MEMBERs.FirstOrDefault(fm => fm.M_NAME == name);
			var person = db.Peoples.FirstOrDefault(p => p.PersonID == personid);
			if (user == null || person == null)
				return Status.BAD;

			PropertyBag["person"] = person;
			AdminController.MailMessage(user, this, "requestlinkage", PropertyBag);
			return Status.OK;
		}

		public void UserLink(int user, int person, bool approved)
		{
			string form = "approved";
			var mm = db.FORUM_MEMBERs.FirstOrDefault(fm => fm.MEMBER_ID == user);
			var pp = db.Peoples.FirstOrDefault(p => p.PersonID == person);
			if (approved)
			{
				if (pp == null || mm == null)
					form = "error";
				else
				{
					pp.FORUM_MEMBER = mm;
//					pp.Member_Name = mm.M_NAME;

					db.SubmitChanges();
					PropertyBag["SEOName"] = Util.SEOEscape(pp.FullName, false);
				}
			}
			else
			{
				form = "rejected";
			}
			PropertyBag["person"] = pp;
			PropertyBag["action"] = form;
			AdminController.MailMessage(mm, this, "linked-" + form, PropertyBag);
			PropertyBag["action"] = form.ToUpper();

		}

		[AjaxAction]
		public void CreatePerson(string name)
		{
			this.HttpContext.Response.ContentType = "application/x-json";
			CancelView();

			int pos = name.IndexOf(", ");
			if (pos == -1)
			{
				RenderText("{\"ID\":-1, \"Message\":\"Bad name Format\"}");
			}
			else
			{
				TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
				string firstName = ti.ToTitleCase(name.Substring(pos + 2));
				string lastName = ti.ToTitleCase(name.Substring(0, pos));
				var existing = from p in db.Peoples
							   where p.FirstName == firstName && p.LastName == lastName
							   select p;

				People person = existing.FirstOrDefault();
				if (person == null)
				{
					person = new People();
					person.FirstName = firstName;
					person.LastName = lastName;
					db.Peoples.InsertOnSubmit(person);
					db.SubmitChanges();
				}
				RenderText("{{\"ID\":{0}, \"Name\":\"{1}\"}}", person.PersonID, person.FullName);
			}
		}
	}
}
